#include <types.h>
#include <stdio.h>
#include <string.h>
#include <console.h>
#include <kdebug.h>
#include <picirq.h>
#include <trap.h>
#include <clock.h>
#include <intr.h>

int kern_init(void) __attribute__((noreturn));

static void switch_to_user(void);
static void switch_to_kernel(void);
static void user_init(void);

int
kern_init(void) {
    extern char edata[], end[];
    memset(edata, 0, end - edata);

    cons_init();                // init the console

    const char *message = "(THU.CST) os is loading ...";
    cprintf("%s\n\n", message);

    print_kerninfo();

    pic_init();                 // init interrupt controller

    clock_init();               // init clock interrupt
    intr_enable();              // enable irq interrupt

    // user/kernel mode switch test
    print_cur_status();
    cprintf("+++ switch to  user  mode +++\n");
    switch_to_user();

    /* do nothing */
    while (1);
}

static void
switch_to_user(void) {
	// Function to leave the kernel mode
	// and enter user mode for once
	
	// Switch to user mode
	asm volatile ("mrs r1, cpsr;"
		"and r1, r1, #0xF0;"
		"msr cpsr_c, r1"
		:::"r1");
	
	user_init();
	// should not come back here
}

// Do something and switches back to user mode (a system call)
static void
switch_to_kernel(void) {
    asm volatile ("swi %0\n" :: "i" (T_SWITCH_TOK));
}



// designed as the first user function
static void
user_init(void) {
	print_cur_status();
    cprintf("+++ switch to kernel mode +++\n");
    switch_to_kernel();
	cprintf("+++ back from kernel mode +++\n");
	print_cur_status();
	while (1);
}

